// Copyright 2010 futomi  http://www.html5.jp/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// perspective.js v0.0.2
// 2010-08-28

/* -------------------------------------------------------------------
* define objects (name space) for this library.
* ----------------------------------------------------------------- */
if( typeof html5jp == 'undefined' ) {
	html5jp = new Object();
}

(function () {

/* -------------------------------------------------------------------
* constructor
* ----------------------------------------------------------------- */
html5jp.perspective = function(context2D, image) {
	// check the arguments
	//if( ! context2D || ! context2D.strokeStyle ) { return; }
	//if( ! image || ! image.width || ! image.height ) { return; }
	// prepare a <canvas> for the image
	var firstBufferCanvas = document.createElement('canvas');
	firstBufferCanvas.width = parseInt(image.width);
	firstBufferCanvas.height = parseInt(image.height);
	var firstContext = firstBufferCanvas.getContext('2d');
	firstContext.drawImage(image, 0, 0, firstBufferCanvas.width, firstBufferCanvas.height);
	// prepare a <canvas> for the transformed image
	var secondBufferCanvas = document.createElement('canvas');
	secondBufferCanvas.width = context2D.canvas.width;
	secondBufferCanvas.height = context2D.canvas.height;
	var secondContext = secondBufferCanvas.getContext('2d');
	// parameters
	this.buffers = {
		context2D: context2D,
		firstBufferCanvas: firstBufferCanvas,
		firstContext: firstContext,
		secondContext: secondContext
	}
};

/* -------------------------------------------------------------------
* prototypes
* ----------------------------------------------------------------- */
var proto = html5jp.perspective.prototype;

/* -------------------------------------------------------------------
* public methods
* ----------------------------------------------------------------- */
proto.draw = function(points) {
	var upperLeftX = points[0][0];
	var upperLeftY = points[0][1];
	var upperRightX = points[1][0];
	var upperRightY = points[1][1];
	var bottomRightX = points[2][0];
	var bottomRightY = points[2][1];
	var bottomLeftX = points[3][0];
	var bottomLeftY = points[3][1];
	// compute the dimension of each side
	var dims = [
		Math.sqrt( Math.pow(upperLeftX-upperRightX, 2) + Math.pow(upperLeftY-upperRightY, 2) ), // top side
		Math.sqrt( Math.pow(upperRightX-bottomRightX, 2) + Math.pow(upperRightY-bottomRightY, 2) ), // right side
		Math.sqrt( Math.pow(bottomRightX-bottomLeftX, 2) + Math.pow(bottomRightY-bottomLeftY, 2) ), // bottom side
		Math.sqrt( Math.pow(bottomLeftX-upperLeftX, 2) + Math.pow(bottomLeftY-upperLeftY, 2) )  // left side
	];
	//
	var firstWidth = this.buffers.firstBufferCanvas.width;
	var firstHeight = this.buffers.firstBufferCanvas.height;
	// specify the index of which dimension is longest
	var base_index = 0;
	var max_scale_rate = 0;
	var zero_num = 0;
	for( var i=0; i<4; i++ ) {
		var rate = 0;
		if( i % 2 ) {
			rate = dims[i] / firstWidth;
		} else {
			rate = dims[i] / firstHeight;
		}
		if( rate > max_scale_rate ) {
			base_index = i;
			max_scale_rate = rate;
		}
		if( dims[i] == 0 ) {
			zero_num ++;
		}
	}
	if(zero_num > 1) { return; }
	//
	var step = 2;
	var cover_step = step * 5;
	//
	var firstContext = this.buffers.firstContext;
	var secondContext = this.buffers.secondContext;
	secondContext.clearRect(0, 0, secondContext.canvas.width, secondContext.canvas.height);
	if(base_index % 2 == 0) { // top or bottom side
		var hContext = this.createCanvasContext(firstWidth, cover_step);
		var hCanvas = hContext.canvas;
		for( var y=0; y<firstHeight; y+=step ) {
			var r = y / firstHeight;
			var sx = upperLeftX + (bottomLeftX-upperLeftX) * r;
			var sy = upperLeftY + (bottomLeftY-upperLeftY) * r;
			var ex = upperRightX + (bottomRightX-upperRightX) * r;
			var ey = upperRightY + (bottomRightY-upperRightY) * r;
			var ag = Math.atan( (ey-sy) / (ex-sx) );
			var sc = Math.sqrt( Math.pow(ex-sx, 2) + Math.pow(ey-sy, 2) ) / firstWidth;
			hContext.setTransform(1, 0, 0, 1, 0, -y);
			hContext.drawImage(firstContext.canvas, 0, 0);
			//
			secondContext.translate(sx, sy);
			secondContext.rotate(ag);
			secondContext.scale(sc, sc);
			secondContext.drawImage(hCanvas, 0, 0);
			//
			secondContext.setTransform(1, 0, 0, 1, 0, 0);
		}
	} else if(base_index % 2 == 1) { // right or left side
		var wContext = this.createCanvasContext(cover_step, oh);
		var wCanvas = wContext.canvas;
		for( var x=0; x<firstWidth; x+=step ) {
			var r =  x / firstWidth;
			var sx = upperLeftX + (upperRightX-upperLeftX) * r;
			var sy = upperLeftY + (upperRightY-upperLeftY) * r;
			var ex = bottomLeftX + (bottomRightX-bottomLeftX) * r;
			var ey = bottomLeftY + (bottomRightY-bottomLeftY) * r;
			var ag = Math.atan( (sx-ex) / (ey-sy) );
			var sc = Math.sqrt( Math.pow(ex-sx, 2) + Math.pow(ey-sy, 2) ) / oh;
			wContext.setTransform(1, 0, 0, 1, -x, 0);
			wContext.drawImage(firstContext.canvas, 0, 0);
			//
			secondContext.translate(sx, sy);
			secondContext.rotate(ag);
			secondContext.scale(sc, sc);
			secondContext.drawImage(wCanvas, 0, 0);
			//
			secondContext.setTransform(1, 0, 0, 1, 0, 0);
		}
	}
	// set a clipping path and draw the transformed image on the destination canvas.
	this.buffers.context2D.save();
	this.setClippingPath(this.buffers.context2D, [[upperLeftX, upperLeftY], [upperRightX, upperRightY], [bottomRightX, bottomRightY], [bottomLeftX, bottomLeftY]]);
	this.buffers.context2D.drawImage(secondContext.canvas, 0, 0);
	this.buffers.context2D.restore();
}

/* -------------------------------------------------------------------
* private methods
* ----------------------------------------------------------------- */

proto.createCanvasContext = function(w, h) {
	var canvas = document.createElement('canvas');
	canvas.width = w;
	canvas.height = h;
	var rContext = canvas.getContext('2d');
	return rContext;
};

proto.setClippingPath = function(context, points) {
	context.beginPath();
	context.moveTo(points[0][0], points[0][1]);
	for( var i=1; i<points.length; i++ ) {
		context.lineTo(points[i][0], points[i][1]);
	}
	context.closePath();
	context.clip();
};

})();
